# -*- coding: utf-8 -*-


# cmap2owl -- Helper application to convert from concept maps to OWL ontologies
# Copyright (c) 2008-2013  Rodrigo Rizzi Starr
#  
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#  
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#  
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.


'''
Contains a set of rules to identify superclass/subclass relationships

@author: Rodrigo Rizzi Starr
@copyright: Copyright © 2008-2013 Rodrigo Rizzi Starr
@license: MIT License
@contact: rodrigo.starr@gmail.com
'''


from mapperRule import MapperRule
from generalizationRule import MultipleGeneralizationQuestion, \
                               GeneralizationQuestion
from infoProvider import SimpleInfoProvider, UnionOfInfoProviders
from actionProvider import ActionProvider, UnionOfActionProviders
from log import log
log.debug("Loaded...")
         
            
class SpecializationRule(MapperRule):
    "Finds generalization and individualization relations"
    
    def __init__(self, db):
        
        # Linking phrases denoting a individualization or specialization relation
        # Both are when the subject of the phrase is more general than the 
        #  object
        # Individualization relates classes to individuals
        # Specializations relates classes to classes
        classifiers = ["specializationPhrases",
                       "individualizationPhrases",
                       ]
          
        # Heuristics to identify generalization/specialization rules
        rules = [
                 "inferenceGroup(individualizationRule,"
                    "[individualization(_, _, _), specialization(_, _, _)])",
                    
                 "specialization(Relation, FromSet, ToSet) :-"
                    "specializationPhrases(Label),"
                    "label(Relation, Label),"
                    "positive(relation(Relation)),"
                    "forall(relationBetween(Relation, Subclass, Superclass), ("
                            "positive(class(Subclass)),"
                            "positive(class(Superclass)))),"
                    "fromSetRelation(Relation, FromSet),"
                    "toSetRelation(Relation, ToSet)",
                    
                 "individualization(Relation, FromSet, ToSet) :-"
                    "individualizationPhrases(Label),"
                    "label(Relation,Label),"
                    "positive(relation(Relation)),"
                    "forall(relationBetween(Relation, Subclass, Individual),("
                            "positive(individual(Individual)),"
                            "positive(class(Subclass)))),"
                    "fromSetRelation(Relation, FromSet),"
                    "toSetRelation(Relation, ToSet)",
                   
                 "doubleSpecialization(Relation, FromSet, ToSet) :- "
                    "possible(specialization(Relation, FromSet, ToSet)),"
                    "possible(individualization(Relation, FromSet, ToSet))",
                 
                 # Individualization consequences  
                 "isTrue(individual(Individual)) :-"
                   "isTrue(individualization(Relation, _, _)),"
                   "toPartRelation(Relation, Individual)",
                   
                 "isTrue(class(Class)) :-"
                   "isTrue(individualization(Relation, _, _)),"
                   "fromPartRelation(Relation, Class)",
                   
                 "classOfIndividualOWL(Individual, Class) :-"
                   "isTrue(individualization(Relation, _, _)),"
                   "relationBetween(Relation, Class, Individual)",

                  "isFalse(relation(Relation)) :-"
                    "isTrue(individualization(Relation, _, _))",
                   
                 # Specialization consequences  
                 "isTrue(class(Class)) :-"
                   "isTrue(specialization(Relation, _, _)),"
                   "fromPartRelation(Relation, Class)",
                   
                 "isTrue(class(Class)) :-"
                   "isTrue(specialization(Relation, _, _)),"
                   "toPartRelation(Relation, Class)",
                   
                 "subclassOWL(Subclass,Superclass) :-"
                    "isTrue(specialization(Relation, _, _)),"
                    "relationBetween(Relation, Superclass, Subclass)",
                    
                 "isFalse(relation(Relation)) :-"
                      "isTrue(specialization(Relation, _, _))",
                    
                    ]
         
        MapperRule.__init__(self, "Specializations", db, rules, classifiers)
          
    def getGeneralizationQuestions(self):
        "Returns a question list"
        
        questions = []
        specializations = list(self.db.query(
                "possible(specialization(Relation, FromSet, ToSet)),"
                "\+ possible(individualization(Relation, FromSet, ToSet))"
                 ))

#        log.debug("  " + str(len(specializations)) + " specializations")
        
        for answer in specializations:
            questions.append(GeneralizationQuestion(
                    self.db,
                    answer,
                    'The relation "<b>%(Relation)s</b>" seems to say that '
                        '"<b>%(ToSet)s</b>" is(are) of '
                        'type "<b>%(FromSet)s</b>". Is it true ?',
                     "specialization(%(Relation)s, FromSet, ToSet)",
                     helpFile = "generalization",
                     ))
              
        return questions
      
    def getIndividualizationQuestions(self):
        """Returns a list of questions if a certain phrase is an
        individualization phrase"""
        
        questions = []
        
        indQuery = "possible(individualization(Relation, FromSet, ToSet))," \
                    "\+ possible(specialization(Relation, FromSet, ToSet))"
        
        indRelations = list(self.db.query(indQuery))
        
#        log.debug("  " + str(len(indRelations)) + " indRelations")
        
        for answer in indRelations:
            answer["Ids"] = [answer["Relation"]]
            questions.append(GeneralizationQuestion(
                self.db,
                answer,
                'The relation "<b>%(Relation)s</b>" seems to say that the '
                    'element(s) "<b>%(ToSet)s</b>" is(are) member(s) of the '
                    'class(es) "<b>%(FromSet)s</b>". Is it true ?',
                'individualization(%(Relation)s, FromSet, ToSet)',
                 helpFile = "classification",
            ))
        
        return questions
      
    def getDoubleQuestions(self):
        """Returns a list of questions if a certain phrase is an
        individualization phrase"""
        
        questions = []
        
        indQuery = "doubleSpecialization(Relation, FromSet, ToSet)"
        
        indRelations = list(self.db.query(indQuery))
        
#        log.debug("  " + str(len(indRelations)) + " indRelations")
        
        for answer in indRelations:
            answer["Ids"] = [answer["Relation"]]
            answer["Ids"].extend(answer["FromSet"])
            answer["Ids"].extend(answer["ToSet"])
            
            
            questions.append(MultipleGeneralizationQuestion(
                self.db,
                answer,
                'Is (are) the element(s) "<b>%(ToSet)s</b>" member(s) or '
                    'a subclass(es) of the class(es) "<b>%(FromSet)s</b>" ?',
                {'Member':
                    'individualization(%(Relation)s, FromSet, ToSet)',
                 'Subclass':
                    'specialization(%(Relation)s, FromSet, ToSet)',},
                 helpFile = "specializationAndMembership",
            ))
        
        return questions
      
    def getQuestions(self):
        """Returns a list of questions if a certain phrase is an
        individualization phrase"""
        
        questions = []
        questions.extend(self.getDoubleQuestions())
        questions.extend(self.getGeneralizationQuestions())
        questions.extend(self.getIndividualizationQuestions())
        
        return questions

    def getInfoProvider(self):
        "Returns an info provider about mereologic relations"
        
        specializationRelationInfo = SimpleInfoProvider(self.db,
                            "isTrue(specialization(%s, _, _))",
                            "This relation is a specialization relation")
        
        individualizationRelationInfo = SimpleInfoProvider(self.db,
                            "isTrue(individualization(%s, _, _))",
                            "This relation is a membership relation")
        
        # The other info is provided by GeneralizationRule
        
        return UnionOfInfoProviders(specializationRelationInfo,
                                    individualizationRelationInfo,)


    def getActionsProvider(self):
        """Returns actions providers for this rule"""
        
        specializationProvider = ActionProvider(self.db,
                                "equivalentLinkingPhrase(Element, %s),"
                                        "label(Element, Label),"
                                        "\+ specializationPhrases(Label)",
                                "Does this phrase "
                                        "indicate specialization ?",
                                "specializationPhrases(%(Label)s)")
        
        individualizationProvider = ActionProvider(self.db,
                                "equivalentLinkingPhrase(Element, %s),"
                                        "label(Element, Label),"
                                        "\+ individualizationPhrases(Label)",
                                "Does this phrase "
                                        "indicate membership ?",
                                "individualizationPhrases(%(Label)s)")
        
        
        return UnionOfActionProviders(specializationProvider,
                                      individualizationProvider)

